package com.thd.springboottest.shironostate.controller;

import com.alibaba.fastjson.JSON;
import com.thd.springboottest.shironostate.bean.Message;
import com.thd.springboottest.shironostate.bean.ShiroUser;
import com.thd.springboottest.shironostate.service.ShiroService;
import com.thd.springboottest.shironostate.token.PhoneMessageToken;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.BearerToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * com.thd.springboottest.shiro.controller.LoginController
 * User: devil13th
 * Date: 2020/1/23
 * Time: 16:43
 * Description: No Description
 */


/**
 * 三种登录方式：
 *     http://127.0.0.1:8899/thd/login?userName=wsl&credential=123456
 *     http://127.0.0.1:8899/thd/pLogin?phone=13800138000&code=a648a0
 */
@RestController
public class LoginController {

    @Autowired
    private ShiroService shiroService;
    @Autowired
    private RedisTemplate redisTemplate;


    private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    protected HttpServletRequest request;
    @Autowired
    protected HttpServletResponse response;

    public Message success(){
        return Message.success();
    }

    public Message success(String msg){
        return Message.success(msg);
    }

    public Message success(Object result){
        return Message.success(result);
    }

    public Message error(){
        return Message.error();
    }

    public Message error(String msg){
        return Message.error(msg);
    }

    public Logger getLogger() {
        return logger;
    }

    public void setLogger(Logger logger) {
        this.logger = logger;
    }

    @RequestMapping("/login")
    // url : http://127.0.0.1:8899/thd/login?userName=wsl&credential=123456
    // url : http://127.0.0.1:8899/thd/login?userName=zhangsan&credential=123456
    public String login(ShiroUser user) {

        this.getLogger().info("login()");
        //添加用户认证信息
        Subject subject = SecurityUtils.getSubject();

        ShiroUser realUser = shiroService.loadUserByAccount(user.getUserName());
        if(realUser == null){
            return "用户未找到";
        }


        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
                user.getUserName(),
                user.getCredential()
        );
        try {
            //进行验证，这里可以捕获异常，然后返回对应信息
            subject.login(usernamePasswordToken);

/*
            this.getLogger().info(" ------------------ 认证 ---------------------");
            String sessionId =  SecurityUtils.getSubject().getSession().getId().toString();
            SecurityUtils.getSubject().getSession().setAttribute("userName",user.getUserName());
            this.getLogger().info("session id:" + sessionId);
            this.getLogger().info("isAuthenticated:" + SecurityUtils.getSubject().isAuthenticated());
            this.getLogger().info("Principal:" + SecurityUtils.getSubject().getPrincipal());
            this.getLogger().info("Principal Json:" + JSON.toJSONString(SecurityUtils.getSubject().getPrincipal()));
            SecurityUtils.getSubject().getSession().setAttribute("user",user);
            SecurityUtils.getSubject().getSession().setAttribute("username",user.getUserName());
            this.getLogger().info("User Json:" + JSON.toJSONString(user));
            this.getLogger().info(SecurityUtils.getSubject().getSession().getAttribute("user").toString());

           // this.getLogger().info(SecurityUtils.getSecurityManager().getSession("myShiroshiro_redis_session:" + sessionId));


            this.getLogger().info("------------------- 权限 ---------------------");
            System.out.println(subject.hasRole("admin"));
            System.out.println(subject.hasRole("user"));
//            subject.checkPermissions("query");
//            subject.checkPermissions("add");

            System.out.println(subject.isPermitted("query"));
            System.out.println(subject.isPermitted("add"));





//            subject.checkRole("admin");
//            subject.checkPermissions("query", "add");

 */


//            List perms = new ArrayList();
//            perms.add("新增");
//            perms.add("保存");
//            perms.add("删除");
//            SecurityUtils.getSubject().getSession().setAttribute("sessionPerms",perms);

            ShiroUser u = (ShiroUser)subject.getPrincipal();

            // token其实是要生成后保存redis，而不是写死哦
            return "login success,生成的token是：" + u.getToken() + ", 后续请求可以在请求header中的token进行设置后访问以便认证使用";
        } catch (AuthenticationException e) {
            e.printStackTrace();
            return "账号或密码错误！";
        } catch (AuthorizationException e) {
            e.printStackTrace();
            return "没有权限";
        }

    }






    @RequestMapping("/logout")
    // url : http://127.0.0.1:8899/thd/logout
    public String logout() {
        this.getLogger().info("isAuthenticated:" + SecurityUtils.getSubject().isAuthenticated());
        SecurityUtils.getSubject().logout();
        this.getLogger().info(" ------------------ 认证 ---------------------");
        String sessionId =  SecurityUtils.getSubject().getSession().getId().toString();
        this.getLogger().info("isAuthenticated:" + SecurityUtils.getSubject().isAuthenticated());
        this.getLogger().info("session id:" + sessionId);
        this.getLogger().info("Principal:" + SecurityUtils.getSubject().getPrincipal());


        return "logout success";
    }

    //注解验角色和权限
    @RequiresRoles("admin")
    @RequiresPermissions("add")
    @RequestMapping("/index")
    // url : http://127.0.0.1:8899/thd/index
    // 不登录直接访问 http://127.0.0.1:8899/thd/index  设置header中的token为112233 或  223344
    public String index() {
        return "index!";
    }



    //测试自定义的带有参数的filter

    @RequestMapping("/params")
    // url : http://127.0.0.1:8899/thd/params
    public String params() {
        return "params!";
    }









    // 使用授权码进行登录
    @RequestMapping("/tokenLogin")
    @ResponseBody
    // url : http://127.0.0.1:8899/thd/tokenLogin?loginToken=112233
    public String tokenLogin(@RequestParam("loginToken") String loginToken){

            try {
                BearerToken token = new BearerToken(loginToken);

                Subject subject = SecurityUtils.getSubject();
                subject.login(token);
                System.out.println(subject.isAuthenticated());
                System.out.println(subject.getPrincipal());


//                Session session = subject.getSession();
//                System.out.println(session.getId());

                this.getLogger().info("------------------- 权限 ---------------------");
                System.out.println(subject.hasRole("admin"));
                System.out.println(subject.hasRole("user"));
                System.out.println(subject.isPermitted("query"));
                System.out.println(subject.isPermitted("add"));


                this.getLogger().info("登陆成功");

                ShiroUser u = (ShiroUser)subject.getPrincipal();

                // token其实是要生成后保存redis，而不是写死哦
                return "login success,生成的token是：" + u.getToken() + ", 后续请求可以在请求header中的token进行设置后访问以便认证使用";


            }catch (AuthenticationException e) {
                e.printStackTrace();
                return "授权码错误！";
            } catch (AuthorizationException e) {
                e.printStackTrace();
                return "没有权限";
            }
    }

    @RequestMapping("/perm/add")
//    @RequiresPermissions(value={"add"})
    @RequiresPermissions("add")
    // url : http://127.0.0.1:8899/thd/perm/add
    public String perm() {

        return "perm/add";
    }
    @RequestMapping("/perm/query")
    @RequiresPermissions("query")
    // url : http://127.0.0.1:8899/thd/perm/query
    public String query() {

        return "perm/query";
    }

    @RequiresRoles("admin")
    @RequestMapping("/role/add")
    // url : http://127.0.0.1:8899/thd/role/add
    public String roleAdd() {

        return "role/add";
    }




    @RequestMapping("/dynamicPerm")
    @RequiresPermissions(value={"dynamicPerm"})
    // url : http://127.0.0.1:8899/thd/dynamicPerm
    public String dynamicPerm() {

        return "perm/query";
    }

    @RequestMapping("/list")
    // url : http://127.0.0.1:8899/thd/list
    public String list() {
//        SecurityUtils.getSubject().getSession().touch();
        SessionKey sk = new DefaultSessionKey(SecurityUtils.getSubject().getSession().getId());
        Session session = SecurityUtils.getSecurityManager().getSession(sk);
//        this.getLogger().info("attribute username:" + session.getAttribute("userName"));
        return "list!";
    }


    @ResponseBody
    @RequestMapping("/unauthorizedurl")
    // url : http://127.0.0.1:8899/thd/unauthorizedurl
    public Map unauthorizedurl() {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("code", "1000000");
        map.put("msg", "未授权,请授权!!");
        return map;
    }
    @ResponseBody
    @RequestMapping("/unlogin")
    // url : http://127.0.0.1:8899/thd/unlogin
    public Map unlogin() {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("code", "1000000");
        map.put("msg", "未登录,请登录 please login!!");
        return map;
    }



    @RequestMapping("/showInfo")
    @ResponseBody
    // url : http://127.0.0.1:8899/thd/showInfo
    public Map showInfo() {




        this.getLogger().info("==================== Information ====================");
//        this.getLogger().info("Subject:" + JSON.toJSONString(SecurityUtils.getSubject()));
        this.getLogger().info("Session:" + JSON.toJSONString(SecurityUtils.getSubject().getSession()));
        this.getLogger().info("Principal:" + JSON.toJSONString(SecurityUtils.getSubject().getPrincipal()));

        if(null != SecurityUtils.getSubject().getPrincipal()){
            this.getLogger().info("principal:" + ((ShiroUser) SecurityUtils.getSubject().getPrincipal()).getUserName());
        }
        this.getLogger().info("isAuthenticated:" + SecurityUtils.getSubject().isAuthenticated());

        Map m = new HashMap();
//        m.put("principal",JSON.toJSONString(SecurityUtils.getSubject().getPrincipal()));
//        m.put("Session",JSON.toJSONString(SecurityUtils.getSubject().getSession()));

        m.put("principal",SecurityUtils.getSubject().getPrincipal());
        m.put("Session",SecurityUtils.getSubject().getSession());
        return m;

    }


    @RequestMapping("/simpleHash")
    // url : http://127.0.0.1:8899/thd/simpleHash?pwd=pa$$word&salt=123456
    public String simpleHash(String pwd,String salt){

        int iteratorCount = 1024;
        Object result = new SimpleHash("MD5",pwd,salt,iteratorCount);
        return result.toString();
    }





    /**
     * 在shiroConfig中使用 map.put("/testRole","prems"); 配置可访问此URL的权限
     * @return
     */
    @RequestMapping("/testPerm")
    public String testPerm(){
        this.getLogger().info("principal:" + ((ShiroUser) SecurityUtils.getSubject().getPrincipal()).getUserName());
        return "testPerm";
    }

    /**
     * 在shiroConfig中使用 map.put("/testRoleAdmin","prems") 配置可访问此URL的权限
     * @return
     *
     * 该请求中如果header有token且值是112233就默认登录了
     */
    @RequestMapping("/testPermAdd")
    public String testPermAdd(){
        this.getLogger().info("principal:" + ((ShiroUser) SecurityUtils.getSubject().getPrincipal()).getUserName());
        return "testPermAdd";
    }
    /**
     * 不在shiroConfig中配置可访问此URL的权限,而是在该方法中加@RequiresPermissions注释
     * @return
     *
     * 该请求中如果header有token且值是112233就默认登录了
     */
    @RequiresPermissions("add")
    @RequestMapping("/testPermAdd2")
    public String testPermAdd2(){
        this.getLogger().info("principal:" + ((ShiroUser) SecurityUtils.getSubject().getPrincipal()).getUserName());
        return "testPermAdd2";
    }

    /**
     * 在shiroConfig中使用 map.put("/testPerm","prems") 配置 可访问此URL的角色
     * @return
     *
     * 该请求中如果header有token且值是112233就默认登录了
     */
    @RequestMapping("/testRole")
    public String testRole(){
        this.getLogger().info("principal:" + ((ShiroUser) SecurityUtils.getSubject().getPrincipal()).getUserName());
        return "testRole";
    }

    /**
     * 在shiroConfig中使用 map.put("/testRoleAdmin","roles[admin]") 可访问此URL的角色
     * @return
     *
     * 该请求中如果header有token且值是112233就默认登录了
     */
    @RequestMapping("/testRoleAdmin")
    public String testRoleAdmin(){
        this.getLogger().info("principal:" + ((ShiroUser) SecurityUtils.getSubject().getPrincipal()).getUserName());
        return "testRoleAdmin";
    }

    /**
     * 不在shiroConfig中配置可访问此URL的角色,而是在该方法中加@RequiresRoles注释
     * 通过注释进行角色验证的过程使用AOP完成的 ， 而不是Filter，验证不通过可以通过定义全局异常处理器(参见ShiroExceptionHandlerController(@RestControllerAdvice注释的Controller))进行处理
     * @return
     *
     * 该请求中如果header有token且值是112233就默认登录了
     */
    @RequiresRoles("admin")
    @RequestMapping("/testRoleAdmin2")
    public String testRoleAdmin2(){
        this.getLogger().info("principal:" + ((ShiroUser) SecurityUtils.getSubject().getPrincipal()).getUserName());
        return "testRoleAdmin2";
    }

    /**
     * 该请求中如果header有token且值是112233就默认登录了
     * @return
     */
    @RequestMapping("/jwt")
    public String jwt(){
        System.out.println("login controller jwt..");
        return "jwt";
    }


    /**
     * 用于测试注解权限校验
     * @return
     *
     * url:http://127.0.0.1:8899/thd/testAuthorizationByAnnotation
     *  注意：通过设置header中的token来决定是哪个用户  token=112233 , token=223344 两个用户
     */
    @RequiresPermissions("add")
    @RequestMapping("/testAuthorizationByAnnotation")
    public String testAuthorizationByAnnotation(){
        this.getLogger().info("principal:" + ((ShiroUser) SecurityUtils.getSubject().getPrincipal()).getUserName());
        return "testPermAdd2";
    }





}
